/* template
 * template
 */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <gio/gio.h>
#include <gio/gnetworking.h>
#include "sql.h"
#include "email.h"
#include "sqlite3.h"
#include "account.h"
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
#include <libsoup/soup.h>
#define DB_NAME "account.db"
#define SERVER_PORT 12222
gpointer account_db = NULL;
GList *clients = NULL;
enum account_type {
  ACCOUNT_TYPE_PERSON,
  ACCOUNT_TYPE_GROUP,
};
enum read_status {
  READ_STATUS_NO,
  READ_STATUS_YES,
};
enum contact_status {
  CONTACT_STATUS_REQUEST,
  CONTACT_STATUS_BE_REQUESTED,
  CONTACT_STATUS_AGREE,
  CONTACT_STATUS_DISAGREE,
};
gchar *err_create (const char *err)
{
  int rc = 0;
  gchar *res = NULL;
  xmlNodePtr root = NULL, node = NULL;
  xmlBufferPtr xmlbuf = NULL;

  root = xmlNewNode (NULL, BAD_CAST "root");
  node = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  xmlNewProp (node, BAD_CAST "error", BAD_CAST err);
  xmlbuf = xmlBufferCreate ();
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error\n");
    goto out;
  }
  res = g_strdup ((char *)xmlbuf->content);
out:
  return res;
}
static char *
email_verification (const gchar *msg, gsize size, void *sql)
{
  xmlDocPtr doc = NULL;
  xmlNodePtr root = NULL, child = NULL;
  xmlChar *email = NULL;
  gpointer res = NULL;//, result = NULL;
  gchar buf[1024] = {0}, verification[32] = {0}, statement[1024] = {0};
  gint rc, row_id;
  //gsize len;
  //time_t t;
  //GDateTime *dt = NULL;

  doc = xmlParseDoc (BAD_CAST msg);
  root = xmlDocGetRootElement (doc);
  child = xmlFirstElementChild (root);
  email = xmlGetProp (child, BAD_CAST "email");
  if (!email) {
    res = err_create ("no email");
    goto out;
  }
  if (strlen ((char *)email) < 1) {
    res = err_create ("no email");
    goto out;
  }
  rc = sql_exist (sql, ACCOUNT_TABLE_ACCOUNT, "email", (char *)email);
  if (rc) {
    snprintf(statement, sizeof(statement),
        "select 1 from '%s' where %s='%s' and username not null limit 1;",
        "account", "email", (char *)email);
    rc = sql_exec_count(sql, statement);
    if (rc) {
      res = err_create ("此邮箱已存在");
      goto out;
    } else {
      rc = g_random_int_range(0, 0xffff);
      g_snprintf(verification, sizeof verification, "%d", rc);
      email_qq_verification ("stmp.qq.com", "210214599@qq.com", "wutffilwggydcaef",
          (char *)email, verification);
      g_snprintf(buf, sizeof(buf),
          "update %s set verification='%s' where email='%s';",
          ACCOUNT_TABLE_ACCOUNT, verification, email);
      res = sql_exec(sql, buf);
    }
  } else {
    rc = g_random_int_range(10000, 0xffff);
    g_snprintf(verification, sizeof verification, "%d", rc);
    email_qq_verification ("stmp.qq.com", "210214599@qq.com", "wutffilwggydcaef",
        (char *)email, verification);
    g_snprintf(buf, sizeof(buf),
        "insert into %s (email,verification) values ('%s','%s');",
        ACCOUNT_TABLE_ACCOUNT, (char *)email, verification);
    res = sql_exec(sql, buf);//verification,id
    g_free (res);
    row_id = sqlite3_last_insert_rowid(account_db);
    //create contact
    g_snprintf(buf, sizeof(buf),
        "CREATE TABLE 'contacts_%"G_GINT32_FORMAT"' ('id'	INTEGER,'limits' INTEGER,	'status'INTEGER);",
        row_id);
    res = sql_exec(sql, buf);//verification,id
    g_free (res);
    //create history
    g_snprintf(buf, sizeof(buf),
        "CREATE TABLE 'history_%"G_GINT32_FORMAT"' (\"id\"	INTEGER, \"send_id\" INTEGER, \"read_id\" INTEGER, \"msg\"	TEXT, \"time\"	INTEGER, \"type\"	TEXT, \"status\" INTEGER);",
        row_id);
    res = sql_exec(sql, buf);//verification,id
    g_free (res);
  }
out:
  if (doc)
    xmlFreeDoc(doc);
  if (email)
    xmlFree (email);
  return res;
}
static char *
post_account_sign_up (const gchar *msg, gsize size, void *sql)
{
  xmlDocPtr doc = NULL;
  xmlNodePtr root = NULL, child = NULL;
  xmlChar *email = NULL, *username = NULL, *verification = NULL, *pwd = NULL;
  gpointer res = NULL, result = NULL;
  gchar buf[1024] = {0};
  gint rc = 0;

  doc = xmlParseDoc (BAD_CAST msg);
  root = xmlDocGetRootElement (doc);
  child = xmlFirstElementChild (root);
  email = xmlGetProp (child, BAD_CAST "email");
  username = xmlGetProp (child, BAD_CAST "username");
  verification = xmlGetProp (child, BAD_CAST "verification");
  pwd = xmlGetProp (child, BAD_CAST "pwd");
  g_snprintf(buf, sizeof(buf),
      "select 1 from %s where email='%s' and verification='%s';",
      "account", (char *)email, (char *)verification);
  rc = sql_exec_count(sql, buf);
  if (rc) {
    g_snprintf(buf, sizeof(buf),
        "select 1 from %s where email='%s' and username not null;",
        "account", (char *)email);
    rc = sql_exec_count(sql, buf);
    if (rc) {
      res = err_create ("此邮箱已存在");
      goto out;
    }
  } else {
    res = err_create ("验证码错误");
    goto out;
  }
  //g_debug("errno:%d\n", errno);
  //t = g_date_time_to_unix(datetime);
  g_snprintf(buf, sizeof(buf),
      "update %s set username='%s',pwd='%s' where email='%s' and verification='%s' and username is null;",
      "account", (char *)username, (char *)pwd, (char *)email,
      (char *)verification);
  res = sql_exec(sql, buf);
  g_free (res);
  g_snprintf(buf, sizeof(buf),
      "select id from %s where email='%s';",
      "account", (char *)email);
  res = sql_exec(sql, buf);
  goto out;
out:
  if (email)
    xmlFree (email);
  if (username)
    xmlFree (username);
  if (pwd)
    xmlFree (pwd);
  if (verification)
    xmlFree (verification);
  if (result)
    g_free (result);
  if (doc)
    xmlFreeDoc(doc);
  return res;
}
static char *
post_account_sign_in (const gchar *msg, gsize size, void *sql)
{
  xmlDocPtr doc = NULL;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  xmlChar *id = NULL, *pwd = NULL;
  gchar buf[1024] = {0}, verification[32] = {0};
  gpointer res = NULL;
  gint rc = 0;

  //g_debug ("sql:%p", sql);
  doc = xmlParseDoc (BAD_CAST msg);
  root = xmlDocGetRootElement (doc);
  child = xmlFirstElementChild (root);
  id = xmlGetProp (child, BAD_CAST "id");
  pwd = xmlGetProp (child, BAD_CAST "pwd");
  root = NULL;
  g_snprintf(buf, sizeof(buf),
      "select 1 from %s where id='%s' and pwd='%s';",
      "account", (char *)id, (char *)pwd);
  rc = sql_exec_count(sql, buf);
  root = xmlNewNode (NULL, BAD_CAST "root");
  child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  xmlbuf = xmlBufferCreate ();
  if (rc) {
    rc = g_random_int_range(0, 0xffff);
    g_snprintf(verification, sizeof verification, "%d", rc);
    g_snprintf(buf, sizeof(buf),
        "update %s set verification='%s' where id='%s' and pwd='%s';",
        "account", verification, (char *)id, (char *)pwd);
    res = sql_exec(sql, buf);
    g_free(res);
    xmlNewProp (child, BAD_CAST "token", BAD_CAST verification);
  } else {
    xmlNewProp (child, BAD_CAST "error", BAD_CAST "id or pwd error");
  }
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error\n");
    goto out;
  }
  if (xmlbuf)
    res = g_strdup ((char *)xmlbuf->content);
  goto out;
out:
  if (xmlbuf)
    xmlBufferFree (xmlbuf);
  if (root)
    xmlFreeNode (root);
  if (doc)
    xmlFreeDoc(doc);
  if (id)
    xmlFree (id);
  if (pwd)
    xmlFree (pwd);
  return res;
}
void o_no_sign (void *o, const gchar *name)
{
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gint rc;

  root = xmlNewNode (NULL, BAD_CAST name);
  child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  xmlNewProp (child, BAD_CAST "error", BAD_CAST "no sign");
  xmlbuf = xmlBufferCreate ();
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error");
    goto out;
  }
  soup_websocket_connection_send_text (o, (char *)xmlbuf->content);
  g_print ("----[websocket]:%s\n", (char *)xmlbuf->content);
out:
  if (xmlbuf)
    xmlBufferFree (xmlbuf);
  if (root)
    xmlFreeNode (root);
  return;
}
gboolean o_sign (void *o, gint id, const gchar *token, const gchar *name)
{
  gboolean rc = FALSE;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gchar buf[1024] = {0};

  g_snprintf(buf, sizeof(buf),
      "select 1 from account where id =%"G_GINT32_FORMAT" and verification='%s';",
      id, token);
  rc = sql_exec_count(account_db, buf);
  root = xmlNewNode (NULL, BAD_CAST name);
  child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  if (!rc) {
    xmlNewProp (child, BAD_CAST "error", BAD_CAST "no sign");
  }
  xmlbuf = xmlBufferCreate ();
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error\n");
    goto out;
  }
  soup_websocket_connection_send_text (o, (char *)xmlbuf->content);
  g_print ("----[websocket]:%s\n", (char *)xmlbuf->content);
out:
  if (xmlbuf)
    xmlBufferFree (xmlbuf);
  if (root)
    xmlFreeNode (root);
  return rc;
}
gboolean o_get_contact (void *o, gint id, const gchar *name)
{
  gboolean rc = FALSE;
  gchar buf[1024] = {0}, *res = NULL;

  g_snprintf(buf, sizeof(buf),
      "select contacts_%"G_GINT32_FORMAT".*,account.username from contacts_%"G_GINT32_FORMAT",account where contacts_%"G_GINT32_FORMAT".id==account.id and contacts_%"G_GINT32_FORMAT".status=%d;",
      id, id, id, id, CONTACT_STATUS_AGREE);
  res = sql_exec_plus(account_db, buf, name);
  soup_websocket_connection_send_text (o, res);
  g_print ("----[websocket]:%s\n", res);
  g_free (res);
  return rc;
}
gboolean o_get_history (void *o, gint id, gint peer_id, const gchar *name)
{
  gboolean rc = FALSE;
  gchar buf[1024] = {0}, *res = NULL;

  g_snprintf(buf, sizeof(buf),
      "select * from history_%"G_GINT32_FORMAT" where send_id==%"G_GINT32_FORMAT" and status=%d;",
      id, peer_id, READ_STATUS_NO);
  res = sql_exec_plus(account_db, buf, name);
  soup_websocket_connection_send_text (o, res);
  g_print ("----[websocket]:%s\n", res);
  g_free (res);
  g_snprintf(buf, sizeof(buf),
      "update history_%"G_GINT32_FORMAT" set status=%d where send_id==%"G_GINT32_FORMAT" or read_id=%"G_GINT32_FORMAT";",
      id, READ_STATUS_YES, peer_id, peer_id);
  res = sql_exec_plus(account_db, buf, name);
  g_free (res);
  return rc;
}
gboolean o_get_chats (void *o, gint id, gint peer_id, const gchar *name)
{
  gboolean rc = FALSE;
  gchar buf[1024] = {0}, *res = NULL;

  g_snprintf(buf, sizeof(buf),
      "select id,status from contacts_%"G_GINT32_FORMAT" where status=%d;",
      id, CONTACT_STATUS_BE_REQUESTED);
  res = sql_exec_plus(account_db, buf, name);
  soup_websocket_connection_send_text (o, res);
  g_print ("----[websocket]:%s\n", res);
  g_free (res);
  return rc;
}
gboolean o_get_no_read (void *o, gint id, gint peer_id, const gchar *name)
{
  gboolean rc = FALSE;
  gchar buf[1024] = {0}, *res = NULL;

  g_snprintf(buf, sizeof(buf),
      "select send_id,count(*) as count from history_%"G_GINT32_FORMAT" where status=%d and send_id!=%"G_GINT32_FORMAT" group by send_id;",
      id, READ_STATUS_NO, id);
  res = sql_exec_plus(account_db, buf, name);
  soup_websocket_connection_send_text (o, res);
  g_print ("----[websocket]:%s\n", res);
  g_free (res);
  return rc;
}
gboolean o_send (void *o, gint id, gint peer_id, const gchar *msg,
    const gchar *name, gint64 time_int64)
{
  gint row_id, *id_p;
  gboolean rc = FALSE;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gchar buf[1024] = {0}, *res = NULL;

  //save to db
  g_snprintf(buf, sizeof(buf),
      "insert into history_%"G_GINT32_FORMAT" (send_id,read_id,msg,type,time) values (%"G_GINT32_FORMAT",%"G_GINT32_FORMAT",'%s','chat',%"G_GINT64_FORMAT");",
      id, id, peer_id, msg, time_int64);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  g_snprintf(buf, sizeof(buf),
      "insert into history_%"G_GINT32_FORMAT" (send_id,read_id,msg,type,time,status) values (%"G_GINT32_FORMAT",%"G_GINT32_FORMAT",'%s','chat',%"G_GINT64_FORMAT",0);",
      peer_id, id, peer_id, msg, time_int64);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  row_id = sqlite3_last_insert_rowid(account_db);
  //create msg
  root = xmlNewNode (NULL, BAD_CAST "read");
  child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, id);
  xmlNewProp (child, BAD_CAST "peer_id", BAD_CAST buf);
  g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, row_id);
  xmlNewProp (child, BAD_CAST "row_id", BAD_CAST buf);
  xmlNewProp (child, BAD_CAST "msg", BAD_CAST msg);
  xmlbuf = xmlBufferCreate ();
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error\n");
    goto out;
  }
  //get contacts id l
  GList *cl = NULL, *l = NULL;
  gint cl_id = 0;
  g_snprintf(buf, sizeof(buf),
      "insert into history_%"G_GINT32_FORMAT" (send_id,read_id,msg,type,time) values (%"G_GINT32_FORMAT",%"G_GINT32_FORMAT",'%s','chat',%"G_GINT64_FORMAT");",
      id, id, peer_id, msg, time_int64);
  cl = sql_int_list_exec(account_db, buf);
  while (cl) {
    cl_id = GPOINTER_TO_INT(cl->data);
    l = clients;
    while (l) {
      id_p = g_object_get_data (G_OBJECT (l->data), "id");
      if (GPOINTER_TO_INT (id_p) == cl_id) {
        soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
      }
      l = l->next;
    }
    cl = cl->next;
  }
out:
  if (xmlbuf)
    xmlBufferFree (xmlbuf);
  if (root)
    xmlFreeNode (root);
  return rc;
}
gboolean o_search (void *o, const gchar *indexes, const gchar *name)
{
  gboolean rc = FALSE;
  gchar buf[1024] = {0}, *res = NULL;

  g_snprintf(buf, sizeof(buf),
      "select id,username from account where id='%s' or username like '%%%s%%';",
      indexes, indexes);
  res = sql_exec_plus(account_db, buf, name);
  soup_websocket_connection_send_text (o, res);
  g_print ("----[websocket]:%s\n", res);
  g_free (res);
  return rc;
}
gboolean o_agree_add_contact (void *o, gint id, gint peer_id, gint status)
{
  gint *id_p;
  gboolean rc = FALSE;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gchar buf[1024] = {0}, *res = NULL;

  if (status) {
    status = CONTACT_STATUS_AGREE;
  } else {
    status = CONTACT_STATUS_DISAGREE;
  }
  g_snprintf(buf, sizeof(buf),
        "update contacts_%"G_GINT32_FORMAT" set status=%d where id=%"G_GINT32_FORMAT";",
        id, status, peer_id);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  g_snprintf(buf, sizeof(buf),
        "update contacts_%"G_GINT32_FORMAT" set status=%d where id=%"G_GINT32_FORMAT";",
        peer_id, status, id);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  GList *l = NULL;
  l = clients;
  while (l) {
    id_p = g_object_get_data (G_OBJECT (l->data), "id");
    if (GPOINTER_TO_SIZE (id_p) == peer_id) {
      root = xmlNewNode (NULL, BAD_CAST "get_contact");
      child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
      g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, id);
      xmlNewProp (child, BAD_CAST "id", BAD_CAST buf);
      xmlbuf = xmlBufferCreate ();
      rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
      if (rc == -1) {
        g_warning ("xmlnodedump error\n");
        goto out;
      }
      soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
      if (xmlbuf)
        xmlBufferFree (xmlbuf);
      if (root)
        xmlFreeNode (root);
    } else if (GPOINTER_TO_SIZE (id_p) == id) {
      root = xmlNewNode (NULL, BAD_CAST "get_contact");
      child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
      g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, peer_id);
      xmlNewProp (child, BAD_CAST "id", BAD_CAST buf);
      xmlbuf = xmlBufferCreate ();
      rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
      if (rc == -1) {
        g_warning ("xmlnodedump error\n");
        goto out;
      }
      soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
      if (xmlbuf)
        xmlBufferFree (xmlbuf);
      if (root)
        xmlFreeNode (root);
    }
    l = l->next;
  }
out:
  return rc;
}
gboolean o_add_contact (void *o, gint id, gint peer_id, gint status,
    const gchar *name, gint64 time_int64)
{
  gint *id_p, owner = 0;
  gboolean rc = FALSE;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gchar buf[1024] = {0}, *res = NULL;
  GList *l = NULL;

  //check group
  g_snprintf(buf, sizeof(buf),
        "select owner from account where id=%"G_GINT32_FORMAT";",
        peer_id);
  owner = sql_int_exec(account_db, buf);
  snprintf(buf, sizeof(buf),
      "select id from contacts_%"G_GINT32_FORMAT" where id=%"G_GINT32_FORMAT";",
      id, peer_id);
  rc = sql_exec_count(account_db, buf);
  if (!rc) {
    g_snprintf(buf, sizeof(buf),
          "insert into contacts_%"G_GINT32_FORMAT" (id,status) values (%"G_GINT32_FORMAT",%d);",
          id, peer_id, CONTACT_STATUS_REQUEST);
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
  } else {
    g_snprintf(buf, sizeof(buf),
          "update contacts_%"G_GINT32_FORMAT" set status=%"G_GINT32_FORMAT" where id=%"G_GINT32_FORMAT";",
          id, CONTACT_STATUS_REQUEST, peer_id);
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
  }
  snprintf(buf, sizeof(buf),
      "select id from contacts_%"G_GINT32_FORMAT" where id=%"G_GINT32_FORMAT";",
      peer_id, id);
  rc = sql_exec_count(account_db, buf);
  if (!rc) {
    g_snprintf(buf, sizeof(buf),
          "insert into contacts_%"G_GINT32_FORMAT" (id,status) values (%"G_GINT32_FORMAT",%d);",
          peer_id, id, CONTACT_STATUS_BE_REQUESTED);
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
  } else {
    g_snprintf(buf, sizeof(buf),
          "update contacts_%"G_GINT32_FORMAT" set status=%"G_GINT32_FORMAT" where id=%"G_GINT32_FORMAT";",
          peer_id, CONTACT_STATUS_BE_REQUESTED, id);
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
  }
  root = xmlNewNode (NULL, BAD_CAST name);
  child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, id);
  xmlNewProp (child, BAD_CAST "peer_id", BAD_CAST buf);
  if (owner) {
    g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, owner);
    xmlNewProp (child, BAD_CAST "group_id", BAD_CAST buf);
  }
  xmlbuf = xmlBufferCreate ();
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error\n");
    goto out;
  }
  l = clients;
  while (l) {
    id_p = g_object_get_data (G_OBJECT (l->data), "id");
    if (owner) {
      if (GPOINTER_TO_SIZE (id_p) == owner) {
        soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
      }
    } else {
      if (GPOINTER_TO_SIZE (id_p) == peer_id) {
        soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
      }
    }
    l = l->next;
  }
out:
  if (xmlbuf)
    xmlBufferFree (xmlbuf);
  if (root)
    xmlFreeNode (root);
  return rc;
}
gboolean o_handle_add_contact (void *o, gint id, gint row_id, gint peer_id,
    gint status, const gchar *name)
{
  gint *id_p;
  gboolean rc = FALSE;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gchar buf[1024] = {0}, *res = NULL;

  g_snprintf(buf, sizeof(buf),
        "update history_%"G_GINT32_FORMAT" set status='1' where id=%"G_GINT32_FORMAT";",
        id, row_id);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  g_snprintf(buf, sizeof(buf),
      "insert into contacts_%"G_GINT32_FORMAT" (id) values (%"G_GINT32_FORMAT");",
      id, peer_id);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  g_snprintf(buf, sizeof(buf),
      "insert into contacts_%"G_GINT32_FORMAT" (id) values (%"G_GINT32_FORMAT");",
      peer_id, id);
  res = sql_exec(account_db, buf);
  if (res)
    free (res);
  if (status) {
    GList *l = NULL;
    l = clients;
    while (l) {
      id_p = g_object_get_data (G_OBJECT (l->data), "id");
      if (GPOINTER_TO_SIZE (id_p) == peer_id) {
        root = xmlNewNode (NULL, BAD_CAST name);
        child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
        g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, id);
        xmlNewProp (child, BAD_CAST "peer_id", BAD_CAST buf);
        xmlbuf = xmlBufferCreate ();
        rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
        if (rc == -1) {
          g_warning ("xmlnodedump error\n");
          goto out;
        }
        soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
        if (xmlbuf)
          xmlBufferFree (xmlbuf);
        if (root)
          xmlFreeNode (root);
      }
      l = l->next;
    }
  }
out:
  return rc;
}
//group
gboolean o_create_group (void *o, gint id, gint peer_id, const gchar *idx, 
    const gchar *name, gint64 time_int64)
{
  gint *id_p, row_id;
  gboolean rc = FALSE;
  xmlNodePtr root = NULL, child = NULL;
  xmlBufferPtr xmlbuf = NULL;
  gchar buf[1024] = {0}, *res = NULL;
  GList *l = NULL;

  root = xmlNewNode (NULL, BAD_CAST name);
  child = xmlNewChild (root, NULL, BAD_CAST "child", NULL);
  snprintf(buf, sizeof(buf),
      "select id from account where type=%"G_GINT32_FORMAT" and owner=%"G_GINT32_FORMAT";",
      ACCOUNT_TYPE_GROUP, id);
  rc = sql_exec_count(account_db, buf);
  if ((rc >= 0) && (rc < 8)) {
    //create account
    g_snprintf(buf, sizeof(buf),
          "insert into account (username,type,owner,email) values ('%s',%"G_GINT32_FORMAT",%"G_GINT32_FORMAT",'group_%"G_GINT32_FORMAT"_%"G_GINT32_FORMAT"');",
          idx, ACCOUNT_TYPE_GROUP, id, id, g_random_int_range(0, 0xffff));
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
    row_id = sqlite3_last_insert_rowid(account_db);
    //create contacts
    g_snprintf(buf, sizeof(buf),
        "CREATE TABLE 'contacts_%"G_GINT32_FORMAT"' ('id' INTEGER,'limits' INTEGER, 'status'INTEGER);",
        row_id);
    res = sql_exec(account_db, buf);//verification,id
    g_free (res);
    //insert owner into group contact
    g_snprintf(buf, sizeof(buf),
          "insert into contacts_%"G_GINT32_FORMAT" (id,status) values (%"G_GINT32_FORMAT",%d);",
          row_id, id, CONTACT_STATUS_AGREE);
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
    //insert group into owner contact
    g_snprintf(buf, sizeof(buf),
          "insert into contacts_%"G_GINT32_FORMAT" (id,status) values (%"G_GINT32_FORMAT",%d);",
          id, row_id, CONTACT_STATUS_AGREE);
    res = sql_exec(account_db, buf);
    if (res)
      free (res);
    //create history
    g_snprintf(buf, sizeof(buf),
        "CREATE TABLE 'history_%"G_GINT32_FORMAT"' (\"id\" INTEGER, \"send_id\" INTEGER, \"read_id\" INTEGER, \"msg\"	TEXT, \"time\"	INTEGER, \"type\"	TEXT, \"status\" INTEGER);",
        row_id);
    res = sql_exec(account_db, buf);//verification,id
    g_free (res);
    //
    g_snprintf (buf, sizeof buf, "%"G_GINT32_FORMAT, row_id);
    xmlNewProp (child, BAD_CAST "id", BAD_CAST buf);
  } else if (rc < 0) {
    xmlNewProp (child, BAD_CAST "error", BAD_CAST "sql error");
  } else {
    g_debug ("L%d f-%s %d", __LINE__, __func__, rc);
    xmlNewProp (child, BAD_CAST "error", BAD_CAST "more than 8 groups");
  }
  xmlbuf = xmlBufferCreate ();
  rc = xmlNodeDump (xmlbuf, NULL, root, 0, 0);
  if (rc == -1) {
    g_warning ("xmlnodedump error\n");
    goto out;
  }
  l = clients;
  while (l) {
    id_p = g_object_get_data (G_OBJECT (l->data), "id");
    if (GPOINTER_TO_SIZE (id_p) == id) {
      soup_websocket_connection_send_text (l->data, (char *)xmlbuf->content);
      if (xmlbuf)
        xmlBufferFree (xmlbuf);
      if (root)
        xmlFreeNode (root);
    }
    l = l->next;
  }
out:
  return rc;
}
void
handle_text (SoupWebsocketConnection *connection, const gchar *data)
{
  xmlDocPtr doc = NULL;
  xmlNodePtr root = NULL, child = NULL;
  xmlChar *tmp_str = NULL;
  gint rc = 0, status = 0, id = 0, read_id = 0, peer_id = 0, row_id = 0,
       read_status = FALSE;
  gint64 time_int64 = 0;
  const gchar *name = NULL;
  gpointer status_p, id_p;

  time_int64 = g_get_real_time ();
  status_p = g_object_get_data (G_OBJECT (connection), "status");
  if (status_p)
    status = GPOINTER_TO_INT (status_p);
  id_p = g_object_get_data (G_OBJECT (connection), "id");
  if (id_p)
    id = GPOINTER_TO_SIZE (id_p);
  doc = xmlParseDoc (BAD_CAST data);
  root = xmlDocGetRootElement (doc);
  child = xmlFirstElementChild (root);

  name = (char *)root->name;
  tmp_str = xmlGetProp (child, BAD_CAST "id");
  if (tmp_str) {
    read_id = atoi ((char *)tmp_str);
    xmlFree (tmp_str);
  }
  tmp_str = xmlGetProp (child, BAD_CAST "peer_id");
  if (tmp_str) {
    peer_id = atoi ((char *)tmp_str);
    xmlFree (tmp_str);
  }
  tmp_str = xmlGetProp (child, BAD_CAST "row_id");
  if (tmp_str) {
    row_id = atoi ((char *)tmp_str);
    xmlFree (tmp_str);
  }
  tmp_str = xmlGetProp(child, BAD_CAST "status");
  if (tmp_str) {
    read_status = atoi ((char *)tmp_str);
    xmlFree (tmp_str);
  }
  if (!g_strcmp0 (name, "sign")) {
    tmp_str = xmlGetProp (child, BAD_CAST "token");
    rc = o_sign (connection, read_id, (char *)tmp_str, name);
    if (tmp_str)
      xmlFree (tmp_str);
    if (rc) {
      g_object_set_data (G_OBJECT (connection), "status", GINT_TO_POINTER(1));
      g_object_set_data (G_OBJECT (connection), "id", GINT_TO_POINTER(read_id));
    }
    goto out;
  }
  if (status < 1) {
    o_no_sign(connection, name);
    goto out;
  }
  if (!g_strcmp0 (name, "get_contact")) {
    o_get_contact (connection, id, name);
  } else if (!g_strcmp0 (name, "get_chats")) {
    o_get_chats (connection, id, peer_id, name);
    o_get_no_read (connection, id, peer_id, "get_no_read");
  } else if (!g_strcmp0 (name, "get_history")) {
    o_get_history (connection, id, peer_id, name);
  } else if (!g_strcmp0 (name, "send")) {
    soup_websocket_connection_send_text (connection, data);
    tmp_str = xmlGetProp(child, BAD_CAST "msg");
    o_send (connection, id, peer_id, (char *)tmp_str, name, time_int64);
    if (tmp_str)
      xmlFree (tmp_str);
  } else if (!g_strcmp0 (name, "search")) {
    tmp_str = xmlGetProp(child, BAD_CAST "index");
    o_search (connection, (char *)tmp_str, name);
    if (tmp_str)
      xmlFree (tmp_str);
  } else if (!g_strcmp0 (name, "add_contact")) {
    o_add_contact (connection, id, peer_id, read_status, name, time_int64);
  } else if (!g_strcmp0 (name, "handle_add_contact")) {
    o_handle_add_contact (connection, id, row_id, peer_id, read_status, name);
  } else if (!g_strcmp0 (name, "agree_add_contact")) {
    o_agree_add_contact (connection, id, peer_id, read_status);
  } else if (!g_strcmp0 (name, "create_group")) {
    tmp_str = xmlGetProp(child, BAD_CAST "index");
    o_create_group (connection, id, peer_id, (char *)tmp_str, name, time_int64);
    if (tmp_str)
      xmlFree (tmp_str);
  } else {
    g_warning ("invalid name:%s", (char *)root->name);
  }
out:
  if (doc)
    xmlFreeDoc(doc);
}
static void
on_text_message (SoupWebsocketConnection *connection,
    SoupWebsocketDataType type,
    GBytes *message,
    gpointer user_data)
{
  gsize len;
  const gchar *data = g_bytes_get_data(message, &len);

  if (type == SOUP_WEBSOCKET_DATA_TEXT) {
    g_print("++++[websocket]: %.*s\n", (int)len, data);
//    soup_websocket_connection_send_text (connection, data);
    handle_text (connection, data);
  }
}
static void
on_close_unref_connection (SoupWebsocketConnection *connection,
    gpointer user_data)
{
  clients = g_list_remove (clients, connection);
  g_print("WebSocket connection closed\n");
  g_clear_object (&connection);
}
static void
got_server_connection (SoupServer *server,
    SoupWebsocketConnection *connection,
    const char *path,
    SoupClientContext *context,
    gpointer user_data)
{
  g_print("WebSocket connected\n");

  clients = g_list_append (clients, connection);
  g_object_ref (connection);
  g_signal_connect(connection, "message", G_CALLBACK(on_text_message), NULL);
  g_signal_connect(connection, "closed", G_CALLBACK(on_close_unref_connection),
      NULL);
}
static void
forget_close (SoupMessage *msg, gpointer user_data)
{
  soup_message_headers_remove (msg->response_headers, "Connection");
}

static void
close_socket (SoupMessage *msg, gpointer user_data)
{
  SoupSocket *sock = user_data;
  int sockfd;
  sockfd = soup_socket_get_fd (sock);
#ifdef G_OS_WIN32
  shutdown (sockfd, SD_SEND);
#else
  shutdown (sockfd, SHUT_WR);
#endif
  soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
                            "foo", 3);
}

static void
timeout_socket (SoupSocket *sock, gpointer user_data)
{
  soup_socket_disconnect (sock);
}
static void
timeout_request_started (SoupServer *server, SoupMessage *msg,
                         SoupClientContext *client, gpointer user_data)
{
  SoupSocket *sock;
  GMainContext *context = g_main_context_get_thread_default ();
  guint readable;

  g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL);

  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
  sock = soup_client_context_get_socket (client);
  G_GNUC_END_IGNORE_DEPRECATIONS;
  readable = g_signal_connect (sock, "readable",
                              G_CALLBACK (timeout_socket), NULL);

  while (soup_socket_is_connected (sock))
          g_main_context_iteration (context, TRUE);
  g_signal_handler_disconnect (sock, readable);
}

static void
setup_timeout_persistent (SoupServer *server, SoupSocket *sock)
{
  char buf[1];
  gsize nread;

  soup_socket_read (sock, buf, 1, &nread, NULL, NULL);
  g_signal_connect (server, "request-started",
                    G_CALLBACK (timeout_request_started), NULL);
}


static void
server_callback (SoupServer *server, SoupMessage *msg,
    const char *path, GHashTable *query,
    SoupClientContext *context, gpointer user_data)
{
  gchar *file_content = NULL;
  gsize file_length = 0;
  GError *error = NULL;

  if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
    soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
    return;
  }

  if (g_str_has_prefix (path, "/content-length/")) {
    gboolean too_long = strcmp (path, "/content-length/long") == 0;
    gboolean no_close = strcmp (path, "/content-length/noclose") == 0;

    soup_message_set_status (msg, SOUP_STATUS_OK);
    soup_message_set_response (msg, "text/plain",
                               SOUP_MEMORY_STATIC, "foobar", 6);
    if (too_long)
      soup_message_headers_set_content_length (msg->response_headers, 9);
    soup_message_headers_append (msg->response_headers,
                                 "Connection", "close");

    if (too_long) {
      SoupSocket *sock;
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
      sock = soup_client_context_get_socket (context);
      G_GNUC_END_IGNORE_DEPRECATIONS;
      g_signal_connect (msg, "wrote-chunk",
                        G_CALLBACK (close_socket), sock);
    } else if (no_close) {
      g_signal_connect (msg, "wrote-headers",
                    G_CALLBACK (forget_close), NULL);
    }
      return;
  }

  if (!strcmp (path, "/timeout-persistent")) {
    SoupSocket *sock;

    G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
    sock = soup_client_context_get_socket (context);
    G_GNUC_END_IGNORE_DEPRECATIONS;
    setup_timeout_persistent (server, sock);
  }

  soup_message_headers_append(msg->response_headers,
      "Access-Control-Allow-Origin", "*");
  gchar *str = NULL;
  if (msg->method == SOUP_METHOD_POST) {
    soup_message_set_status (msg, SOUP_STATUS_OK);
    g_debug ("post path:%s", path);
    g_debug ("post data:%s", msg->request_body->data);
    if (!g_strcmp0(path, "/ts/email/verification")) {
      str = email_verification (msg->request_body->data, 0, user_data);
    } else if (!g_strcmp0(path, "/ts/account/verification")) {
      str = email_verification (msg->request_body->data, 0, user_data);
    } else if (!g_strcmp0(path, "/ts/account/sign_up")) {
      str = post_account_sign_up (msg->request_body->data, 0, user_data);
    } else if (!g_strcmp0(path, "/ts/account/sign_in")) {
      str = post_account_sign_in (msg->request_body->data, 0, user_data);
    } else if (!g_strcmp0(path, "/ts/forum/insert")) {
   //   str = forum_insert (buffer, bufsize, user_data);
    } else if (!g_strcmp0(path, "/ts/forum/select")) {
    //  str = forum_select (buffer, bufsize, user_data);
  //group
    } else if (!g_strcmp0(path, "/ts/sql/group/select")) {
    //  str = forum_select (buffer, bufsize, user_data);
    } else if (!g_strcmp0(path, "/ts/sql/group/insert")) {
    } else if (!g_strcmp0(path, "/ts/sql/group/delete")) {
    } else {
      g_warning ("invalid path:%s", path);
    }
  } else if (msg->method == SOUP_METHOD_GET) {
    g_file_get_contents (path + 1, &file_content, &file_length, &error);
    g_debug ("path:%s file_length :%"G_GSIZE_FORMAT, path + 1, file_length);
    soup_message_body_append(msg->response_body, SOUP_MEMORY_TAKE,
                                     file_content, file_length);
    soup_message_set_status (msg, SOUP_STATUS_OK);
  } else {
    g_debug ("path :%s", path);
    str = g_strdup ("waitting to support.");
  }
  if (str) {
    soup_message_set_response (msg, "text/plain",
                               SOUP_MEMORY_STATIC, str, strlen (str));
    g_debug ("L%d f-%s response:%s", __LINE__, __func__, str);
  }
  goto out;
out:
  return;
}
int
main (int argc, char *argv[])
{
	g_setenv ("GSETTINGS_SCHEMA_DIR", ".", FALSE);
	g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);

  int rc = 0;
//sql
	sqlite3 *sql = NULL;
  rc = sqlite3_open(DB_NAME, &sql);
  if (rc != SQLITE_OK) {
    perror(sqlite3_errmsg(sql));
    goto out;
  }
	g_print("sqlname:%s %p\n", DB_NAME, sql);
  account_db = sql;
//server
  SoupServer *server = soup_server_new(SOUP_SERVER_SERVER_HEADER,
      "MyWebSocketServer", NULL);
  soup_server_add_handler(server, NULL, server_callback, (void *)sql, NULL);
  soup_server_add_websocket_handler(server, "/websocket", NULL,
      NULL, got_server_connection, NULL, NULL);
  soup_server_listen_all(server, SERVER_PORT, 0, NULL);
  g_debug ("server port:%d", SERVER_PORT);

  GMainLoop *loop = g_main_loop_new(NULL, FALSE);

  g_main_loop_run(loop);
  g_main_loop_unref(loop);
  g_object_unref(server);
out:
  return rc;
}
